
#include <BSpace>
#include <BStream>
#include <BNode>
#include <BPhysics>

#include "member_BNode.h"
#include "member_BObject.h"
#include "member_BAnimation.h"

using namespace BWE;

#define member					(*(member_BNode*)_ptr)
#define member_allocate()		_ptr = new member_BNode(this)
#define member_release()		delete (member_BNode*)_ptr

BNode::BNode(const BString& name) : BObject(name)
{
	member_allocate();
}
BNode::BNode()
{
	member_allocate();
}
BNode::~BNode()
{
	member_release();
}

void BNode::setVisible(bool visible)
{
	if (member.visible != visible)
	{
		member.visible = visible;
		if (visible)
			emit(Signal_Show);
		else
			emit(Signal_Hide);
	}
}
bool BNode::visible() const
{
	return member.visible;
}

void BNode::show()
{
	setVisible(true);
}
void BNode::hide()
{
	setVisible(false);
}

void BNode::move(const BVector& move)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(matrix.position() + move);
	setMatrix(matrix);
}
void BNode::move(float x, float y, float z)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(matrix.position() + BVector(x, y, z));
	setMatrix(matrix);
}

void BNode::setPosition(const BVector& position)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(position);
	setMatrix(matrix);
}
void BNode::setPosition(float x, float y, float z)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(x, y, z);
	setMatrix(matrix);
}
BVector BNode::position() const
{
	return member.matrix.position();
}

void BNode::setRotate(float rx, float ry, float rz)
{
	BQuater xrotate(rx, BVector(1, 0, 0));
	BQuater yrotate(ry, BVector(0, 1, 0));
	BQuater zrotate(rz, BVector(0, 0, 1));
	BMatrix matrix = member.matrix;
	matrix.setRotate(xrotate * yrotate * zrotate);
	setMatrix(matrix);
}
void BNode::setRotate(const BQuater& quater)
{
	BMatrix matrix = member.matrix;
	matrix.setRotate(quater);
	setMatrix(matrix);
}
BQuater BNode::rotate() const
{
	return member.matrix.rotate();
}

void BNode::setScale(const BVectorf& scale)
{
	BMatrix matrix = member.matrix;
	matrix.setScale(scale);
	setMatrix(matrix);
}
void BNode::setScale(float x, float y, float z)
{
	BMatrix matrix = member.matrix;
	matrix.setScale(x, y, z);
	setMatrix(matrix);
}
BVector BNode::scaling() const
{
	return member.matrix.scale();
}

void BNode::setMatrix(const BMatrix& matrix)
{
	member.matrix = matrix;
}
const BMatrix& BNode::matrix() const
{
	return member.matrix;
}

void BNode::dirtySpace()
{
	member.dirtySpace = true;
}
const BSpace& BNode::space() const
{
	if(member.dirtySpace)
	{
		around(member.space);
		for (int i = 0; i < member.shapeItems.size(); i++)
		{
			BShape* shape = member.shapeItems[i].shape;
			member.space.expand(shape->space());
		}
		member.dirtySpace = false;
	}
	return member.space;
}

void BNode::setPhysics(BPhysics* physics)
{
	if (member.physics != physics)
	{
		member.physics = physics;
	}
}
BPhysics* BNode::physics() const
{
	return member.physics;
}

void BNode::setState(BState* state)
{
	if (member.state != state)
	{
		member.state = state;
	}
}
BState* BNode::state() const
{
	return member.state;
}

void BNode::addShape(BShape* shape)
{
	if (!shape)
		return;
	if (!member.shapeItems.contain(shape))
	{
		member.shapeItems.append(shape);
		member.connect(shape, Signal_SpaceChanged, &member_BNode::procShapeSpaceChanged);
		member.dirtySpace = true;
	}
}
bool BNode::removeShape(BShape* shape)
{
	if (member.shapeItems.contain(shape))
	{
		BShapeHolder holder = shape;
		member.shapeItems.remove(shape);
		member.disconnect(shape);
		member.dirtySpace = true;
		return true;
	}
	return false;
}
void BNode::clearShapes()
{
	for (int i = 0; i < member.shapeItems.size(); i++)
	{
		BShape* shape = member.shapeItems[i].shape;
		member.disconnect(shape);
	}
	member.shapeItems.clear();
	member.dirtySpace = true;
}

void BNode::setShapeVisible(const BShape* shape, bool visible)
{
	for (int i = 0; i < member.shapeItems.size(); i++)
	{
		ShapeItem& item = member.shapeItems[i];
		if (item.shape == shape)
		{
			item.visible = visible;
		}
	}
}
bool BNode::shapeVisible(const BShape* shape) const
{
	for (int i = 0; i < member.shapeItems.size(); i++)
	{
		ShapeItem& item = member.shapeItems[i];
		if (item.shape == shape)
		{
			return item.visible;
		}
	}
	return false;
}

void BNode::setShapeCollible(const BShape* shape, bool collible)
{
	for (int i = 0; i < member.shapeItems.size(); i++)
	{
		ShapeItem& item = member.shapeItems[i];
		if (item.shape == shape)
		{
			item.collible = collible;
		}
	}
}
bool BNode::shapeCollible(const BShape* shape) const
{
	for (int i = 0; i < member.shapeItems.size(); i++)
	{
		ShapeItem& item = member.shapeItems[i];
		if (item.shape == shape)
		{
			return item.collible;
		}
	}
	return false;
}

bool BNode::setShapeMatrix(const BShape* shape, const BMatrix& matrix)
{
	for (int i = 0; i < member.shapeItems.size(); i++)
	{
		ShapeItem& item = member.shapeItems[i];
		if (item.shape == shape)
		{
			if (item.matrix != matrix)
			{
				item.matrix = matrix;
				member.dirtySpace = true;
			}
			return true;
		}
	}
	return false;
}
BMatrix BNode::shapeMatrix(const BShape* shape)
{
	for (int i = 0; i < member.shapeItems.size(); i++)
	{
		ShapeItem& item = member.shapeItems[i];
		if (item.shape == shape)
		{
			return item.matrix;
		}
	}
	return BMatrix();
}

bool BNode::contains(const BShape* shape) const
{
	return member.shapeItems.contain(shape);
}

int BNode::shapeCount() const
{
	return member.shapeItems.size();
}
BShape* BNode::shape(int index)
{
	return member.shapeItems(index).shape;
}
const BShape* BNode::shape(int index) const
{
	return member.shapeItems(index).shape;
}

BShape* BNode::shape(const BString& name)
{
	for (int i = 0; i < member.shapeItems.size(); i++)
	{
		BShape* shape = member.shapeItems[i].shape;
		if (shape->name() == name)
			return shape;
	}
	return 0;
}
const BShape* BNode::shape(const BString& name) const
{
	for (int i = 0; i < member.shapeItems.size(); i++)
	{
		BShape* shape = member.shapeItems[i].shape;
		if (shape->name() == name)
			return shape;
	}
	return 0;
}

void BNode::addAnimation(BAnimation* animation)
{
	if (!member.animations.contain(animation))
	{
		member.animations.append(animation);
		animation_member(animation)->detach();
		animation_member(animation)->attach(this);
	}
}
void BNode::removeAnimation(BAnimation* animation)
{
	if (member.animations.contain(animation))
	{
		BAnimationHolder holder = animation;
		animation_member(animation)->detach();
		member.animations.remove(animation);
	}
}
void BNode::clearAnimations()
{
	member.animations.clear();
}

bool BNode::contains(const BAnimation* animation) const
{
	return member.animations.contain(animation);
}
int BNode::animationCount() const
{
	return member.animations.size();
}
BAnimation* BNode::animation(int index)
{
	return member.animations(index);
}
const BAnimation* BNode::animation(int index) const
{
	return member.animations(index);
}

void BNode::around(BSpace& space) const
{
	member.space.reset(BVector());
}
void BNode::event(const BEvent& event)
{

}
void BNode::update(const BEvent& event)
{

}
void BNode::render(BRender& render)
{

}
